// Filename: nearly_zero.h
// Created by:  drose (08Mar00)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#ifndef NEARLY_ZERO_H
#define NEARLY_ZERO_H

#include "dtoolbase.h"

// The following two functions are defined just to make the
// NEARLY_ZERO() macro work.  They each return a suitable nearly-zero
// value for their corresponding numeric type.

// Note that declaring these small numeric values first as a static
// const identifier, and then returning the value of that identifier,
// seems to lead to compilation errors (at least in VC7) in which
// sometimes IS_THRESHOLD_COMPEQ(a, a, get_nearly_zero_value(a)) != 0.
CONSTEXPR double
get_nearly_zero_value(double) {
  return 1.0e-12;
}

CONSTEXPR float
get_nearly_zero_value(float) {
  return 1.0e-6f;
}

CONSTEXPR int
get_nearly_zero_value(int) {
  // This is a bit silly, but we should nevertheless define it in
  // case it is called for an integer type.
  return 0;
}


// IS_THRESHOLD_ZERO(value, threshold) returns true if the value is
// within threshold of zero.
#define IS_THRESHOLD_ZERO(value, threshold) \
  ((value) < (threshold) && (value) > -(threshold))

// IS_THRESHOLD_EQUAL(value1, value2, threshold) returns true if the
// two values are within threshold of each other.
#define IS_THRESHOLD_EQUAL(value1, value2, threshold) \
  (IS_THRESHOLD_ZERO((value1) - (value2), threshold))

// IS_THRESHOLD_COMPEQ(value1, value2, threshold) returns true if
// the two values are equal within threshold tolerance.  Unlike
// IS_THRESHOLD_EQUAL, the transitive principle is guaranteed:
// IS_THRESHOLD_COMPEQ(a, b, t) && IS_THRESHOLD_COMPEQ(b, c, t)
// implies IS_THRESHOLD_COMPEQ(a, c, t).
#define IS_THRESHOLD_COMPEQ(value1, value2, threshold) \
  (cfloor(value1 / threshold + 0.5f) == cfloor(value2 / threshold + 0.5f))

// NEARLY_ZERO(float) returns a number that is considered to be so
// close to zero as not to matter for a float.  NEARLY_ZERO(double)
// returns a similar, smaller number for a double.
#define NEARLY_ZERO(FLOATTYPE) (get_nearly_zero_value((FLOATTYPE)0))

// IS_NEARLY_ZERO(value) returns true if the value is very close to
// zero.
#define IS_NEARLY_ZERO(value) \
  (IS_THRESHOLD_ZERO(value, get_nearly_zero_value(value)))

// IS_NEARLY_EQUAL(value1, value2) returns true if the two values are
// very close to each other.
#define IS_NEARLY_EQUAL(value1, value2) \
   (IS_THRESHOLD_EQUAL(value1, value2, get_nearly_zero_value(value1)))


// MAYBE_ZERO(value) returns 0 if the value is nearly zero, and the
// value itself otherwise.
#define MAYBE_ZERO(value) \
  (IS_NEARLY_ZERO(value) ? 0 : (value))


#endif

